package pl.edu.agh.neuraleconomy.core.experiment;

import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import lombok.Getter;
import lombok.Setter;

import org.apache.log4j.Logger;

import pl.edu.agh.neuraleconomy.common.utils.DateUtils;
import pl.edu.agh.neuraleconomy.core.decision.IDecisionMaker;
import pl.edu.agh.neuraleconomy.core.exception.CoreException;
import pl.edu.agh.neuraleconomy.core.experiment.filter.ICompanyFilter;
import pl.edu.agh.neuraleconomy.core.experiment.statistics.IStatistic;
import pl.edu.agh.neuraleconomy.core.simulation.SimulationEngine;
import pl.edu.agh.neuraleconomy.core.simulation.SimulationService;
import pl.edu.agh.neuraleconomy.core.simulation.Transaction;
import pl.edu.agh.neuraleconomy.core.ta.advice.Advice;
import pl.edu.agh.neuraleconomy.core.ta.advice.IAdvisor;
import pl.edu.agh.neuraleconomy.model.exchange.Company;
import pl.edu.agh.neuraleconomy.persistence.base.DaoProvider;
import pl.edu.agh.neuraleconomy.persistence.exchange.CompanyDao;

public class Experiment {
	@Setter
	private IDecisionMaker decisionMaker;
	@Setter
	private IAdvisor advisor;
	@Setter
	private Date endDate;
	@Setter
	@Getter
	private SimulationEngine simulation;

	private SimulationService service = new SimulationService();

	private List<ICompanyFilter> companyFilters = new LinkedList<ICompanyFilter>();

	@Getter
	@Setter
	private List<Company> companies;

	@Getter
	private List<IStatistic> statistics = new LinkedList<IStatistic>();

	private Logger logger = Logger.getLogger(getClass());

	public void start() {
		try {
			initCompanies();

			while (simulation.getCurrentDate().compareTo(endDate) <= 0) {
				makeIteration();
				simulation.nextDay();
			}
		} catch (CoreException e) {
			logger.error(e.getMessage());
		} finally {
			finalizeStatistics();
		}
	}

	public void addCompanyFilter(ICompanyFilter filter) {
		companyFilters.add(filter);
	}

	private void makeIteration() throws CoreException {
		Date currentDate = simulation.getCurrentDate();
		if (isHoliday(currentDate)) {
			logger.info(DateUtils.formatDate(currentDate).concat(" is a holiday"));
			return;
		}
		List<Advice> advices = getAdvices(currentDate);
		List<Transaction> transactionsMade = decisionMaker.makeDecision(simulation, advices);
		prepareStatistics(currentDate, simulation, advices, transactionsMade);
	}

	private List<Advice> getAdvices(Date date) {
		List<Advice> result = new LinkedList<Advice>();
		for (Company c : companies) {
			Advice advice = null;

			try {
				advice = advisor.getAdvice(c, date);
			} catch (Exception e) {
				logger.warn(e.getMessage(), e);
			}

			if (advice != null) {
				result.add(advice);
			}
		}

		return result;

	}

	private void prepareStatistics(Date date, SimulationEngine simulation, List<Advice> advices, List<Transaction> transactionsMade) {
		for (IStatistic stat : statistics) {
			stat.iteration(date, simulation, advices, transactionsMade);
		}
	}

	private void finalizeStatistics() {
		for (IStatistic stat : statistics) {
			stat.finalizeStatistic();
		}
	}

	private boolean isHoliday(Date date) {
		return service.isHoliday(date);
	}

	private void initCompanies() {
		if (companies == null) {

			CompanyDao compDao = DaoProvider.getCompanyDao();

			List<Company> allCompanies = compDao.getAll();
			companies = allCompanies;

			for (ICompanyFilter filter : companyFilters) {
				companies = filter.filter(companies);
			}
		}
	}

}
